/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ebookmgr;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;

import static ebookmgr.Configuration.getSelectedFileExtension;

/**
 *
 * @author mazhao
 */
public class ImportUtil {

    // Ebook suffix
    private static final List<String> EBOOK_SUFFIX;

    static {
        EBOOK_SUFFIX = getSelectedFileExtension();
    }


    /**
     * if this file's type is in ebook.
     *
     * @param filePath file's full path with folder and extension
     * @return true if is ebook, false else
     */
    public static boolean isEBook(String filePath) {
        // get file suffix
        int index = filePath.lastIndexOf(".");

        if (index < 0) {
            return false;
        }

        String suffix = filePath.substring(index);

        // compare suffix with each predefined suffix
        for (String eachSuf : EBOOK_SUFFIX) {
            // match
            if (eachSuf.equalsIgnoreCase(suffix)) {
                return true;
            }
        }
        // no match
        return false;
    }

    /**
     * input:
     *   d:\temp\ebooks
     * output
     *   {
     *     d:\temp\ebooks\Programming Java.pdf
     *     d:\temp\ebooks\Programming Python.pdf
     *     d:\temp\ebooks\ruby\Programming Ruby.pdf
     *   }
     * @param folder folder path
     * @return list of file path
     */
    public static List<String> findFiles(String folder) {
        List<String> allFiles = new ArrayList<String>();

        File root = new File(folder);

        // if this is a file, add and return
        if (root.isFile() && isEBook(folder)) {
            allFiles.add(folder);
            // if this is a folder
        } else {
            // list all files
            File[] files = root.listFiles(new FileFilter() {

                @Override
                public boolean accept(File pathname) {
                    return pathname.isFile();
                }
            });

            // if is ebook, add
            for (File curFile : files) {
                if (isEBook(curFile.getAbsolutePath())) {
                    allFiles.add(curFile.getAbsolutePath());
                }
            }

            // list all folders
            File[] folders = root.listFiles(new FileFilter() {

                @Override
                public boolean accept(File pathname) {
                    return pathname.isDirectory();
                }
            });

            // each invoke findFiles recursively.
            for (File curFolder : folders) {
                // recursively!!!
                List<String> filesInThisFolder = findFiles(curFolder.getAbsolutePath());
                allFiles.addAll(filesInThisFolder);
            }
        }

        return allFiles;
    }

    /**
     * from selection path to string path.
     * @param path
     * @return
     */
    private static String formatPath(TreeNode[] path) {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < path.length; i++) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) path[i];
            sb.append(System.getProperty("file.separator")).append(((CatalogUserObject) node.getUserObject()).getName());
        }

        return sb.toString();
    }

    /**
     * from tree mode get <path, keyword list> map.
     * @param root
     * @return
     */
    public static Map<String, List<String>> parseTreeModel(DefaultMutableTreeNode root) {
        Map<String, List<String>> ret = new HashMap<String, List<String>>();

        int childCnt = root.getChildCount();
        for (int i = 0; i < childCnt; i++) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) root.getChildAt(i);
            // if is leaf: format and put
            if (node.isLeaf()) {
                TreeNode[] path = (TreeNode[]) node.getPath();

                // get path string
                String pathStr = formatPath(path);

                // get key word list
                CatalogUserObject userObj = (CatalogUserObject) node.getUserObject();
                String keyword = userObj.getKeyword();
                String[] keywords = keyword.split(",");
                List<String> keywordList = Arrays.asList(keywords);

                // put to map
                ret.put(pathStr, keywordList);

                // if is not leaf, invoke recursively and put all.
            } else {

                Map<String, List<String>> tempRet = parseTreeModel(node);
                ret.putAll(tempRet);
            }
        }

        return ret;
    }

    private static boolean isMatch(String fileName, List<String> keywords) {
        // 1. remove suffix
        fileName = fileName.substring(0, fileName.lastIndexOf("."));

        // 2. get file name items
        // @TODO: only for " ", furture may be always for "."
        String[] items = fileName.split(" ");

        for (String item : items) {
            if (keywords.contains(item)) {
                return true;
            }
        }

        return false;
    }


    /**
     * input files:
     *   a list of real file path to be inported.
     * input tm:
     *   folder tree model
     * input baseFolder:
     *   to base folder
     * 
     * @param files
     * @param tm
     * @param baseFolder
     * @return
     */
    public static Map<String, String> mapFiles(List<String> files, TreeModel tm, String baseFolder) {
        Map<String, String> ret = new HashMap<String, String>();

        DefaultMutableTreeNode root = (DefaultMutableTreeNode) tm.getRoot();
        Map<String, List<String>> pathAndKeyword = parseTreeModel(root);

        for (String eachFile : files) {

            String fileName = (new File(eachFile)).getName();

            // each folder and keywords
            Iterator<Entry<String, List<String>>> it = pathAndKeyword.entrySet().iterator();
            while (it.hasNext()) {

                Entry<String, List<String>> ent = it.next();
                List<String> keywords = ent.getValue();

                // file name match this folder and keywords
                if (isMatch(fileName, keywords)) {
                    ret.put(eachFile, baseFolder + ent.getKey());
                    // break while, next file
                    break;
                }
            }
        }
        return ret;
    }

    /**
     * simple copy file.
     * @param fromFile
     * @param toFolder
     */
    public static void copyFile(String fromFile, String toFolder) {

        InputStream in = null;
        OutputStream out = null;

        try {
            File targetFolder = new File(toFolder);
            if (!targetFolder.exists()) {
                targetFolder.mkdirs();
            }

            File from = new File(fromFile);
            File to = new File(toFolder, from.getName());

            in = new FileInputStream(from);
            out = new FileOutputStream(to);

            byte[] buf = new byte[Configuration.BUFFER_SIZE];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        }catch(IOException ex){
            EBookMgrUtil.showMessageAndExit("Can not copy file from " + fromFile + " to " + toFolder
                    + ", error message is:" + ex.getMessage());
        } finally {
            EBookMgrUtil.safelyClose(in);
            EBookMgrUtil.safelyClose(out);
        }
    }

    /**
     * import files
     * @param fromTo
     * @throws IOException
     */
    public static void importFiles(Map<String, String> fromTo, boolean move) throws IOException {
        Iterator<Entry<String, String>> it = fromTo.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, String> ent = it.next();
            if(move ) {
                File from = new File(ent.getKey());
                File toFolder = new File(ent.getValue());
                if(!toFolder.exists()) {
                    boolean folderOk = toFolder.mkdirs();
                    if(!folderOk) {
                        System.out.println("can not create to folder!");
                    }
                }
                
                File to = new File(toFolder.getAbsolutePath(), from.getName());
                boolean moveOk = from.renameTo(to);

                if(!moveOk) {
                    System.out.println("can not move file!");
                    System.out.println("from:" + ent.getKey());
                    System.out.println("to:" + ent.getValue());
                }
            } else {
                copyFile(ent.getKey(), ent.getValue());
            }
            System.out.println("copy file:" + ent.getKey());
        }
    }
}
